#!/bin/bash

# This implementation is not based on SED.
# It is made in plain bash 3.0

# ---------
# Variables
# ---------
MYNAME=$0
MYPATH=$(dirname $MYNAME)

ARGLIST=""
OPT=""
OPTVALS=""

# For each option listed, a variable
# with the same name will contain the
# provided value using a pointer mechanism
# using the variable OPT, containing the
# current option.
OPTLIST="tpl sep args values out"

# ---------
# Functions
# ---------
# Perform a left shift of a
# token separated string.
# $1: the string to shift
# $2: separator string
function left_shift {
  str="$1"
  str_after="${str#*$2}"
  # Maybe there was only one element:
  # in that case the shift did nothing
  # so simply set the null string
  if [ "$str_after" = "$str" ]
  then
    str_after=""
  fi
  
  # Output result for caller
  echo "$str_after"
}

# Store the option in $OPT if valid
function read_option {
  # Extract the first word because we get the whole
  # option string as input
  opt="${ARGLIST%% *}"
  # Check the presence of the single option switch '-'
  if [ ! "${opt:0:1}" = "-" ]
  then
    echo "Option expected, found: $opt"
    return 1
  fi
  # Check that the option is in the set of valid options
  for itr in $OPTLIST
  do
    if [ "${opt:1}" = "$itr" ]
    then
      OPT="$itr"
      VALPTR=$OPT
      # Shift
      ARGLIST=$(left_shift "$ARGLIST" " ")
      return 0
    fi
  done
  # Not in the set
  echo "Unrecognized option: $opt"
  return 1
}

# Store the list of values in $OPTVALS if valid
function read_values {
  # Reset the list of options
  OPTVALS=""
  # Read the space separated list of values.
  # At least one value must be found.
  if [ -z "$ARGLIST" ]
  then
    echo "Value expected, found nothing."
    return 1
  fi

  for val in $ARGLIST
  do
    # Check the presence of the single option switch '-'
    if [ "${val:0:1}" = "-" ]
    then
      # Found an option, and no value has been found:
      # this is an error.
      if [ -z "$OPTVALS" ]
      then
        echo "Value expected, found an option instead: $val"
        return 1
      fi
      
      # Found a option, but some values have been read:
      # it's ok.
      break
    else
      if [ ! -z "$OPTVALS" ]; then OPTVALS+=" "; fi
      OPTVALS+="$val"
      # Shift
      ARGLIST=$(left_shift "$ARGLIST" " ")
    fi
  done

  return 0
}

function parse_args {
  ARGLIST=$*
  
  while [ ! -z "$ARGLIST" ]
  do
    #echo "Arglist: $ARGLIST"
    # Attempt to read a valid option
    # and store it in $OPT if valid
    read_option || help_and_quit
    #echo "Option found: $OPT"
    # Attempt to read a valid list of values
    # according to the current option and
    # store it in $OPTVALS if valid
    read_values || help_and_quit
    #echo "Option values found: $OPTVALS"

    # Store the values read in the current
    # variable with the same name as the current option
    # name. Example: option "tpl" will be stored in a
    # bash variable named "tpl".
    eval $OPT='$OPTVALS'
    
  done
}

function debug_args {
  for opt in $OPTLIST
  do
    echo "--- Option: $opt = ${!opt}"
  done
}

function check_args {
  if [ ! -f "$tpl" ]
  then
    echo "Provided template file is not a regular file: \"$tpl\"."
    exit 1
  fi
  if [ ! -r "$tpl" ]
  then
    echo "Provided template file is not readable: \"$tpl\"."
    exit 1
  fi
  outdir=$(dirname "$out")
  if [ ! -x "$outdir" ]
  then
    echo "Output dir is not searchable (x permission missing): \"$outdir\"."
    exit 1
  fi
  if [ ! -w "$outdir" ]
  then
    echo "Output dir is not writable: \"$outdir\"."
    exit 1
  fi
}

function apply_template {
  # Make a copy of the value list
  # because it's gonna be shifted
  valuelist="$values"
  while read line
  do
    # Replace each template parameter
    # by its value
    values="$valuelist"
    subst="$line"
    for rawarg in $args
    do
      # Surround the current parameter
      # by the specified separator
      arg="$sep$rawarg$sep"
      # Read the associated value then
      # shift the value list
      val="${values%%${sep}*}"
      values=$(left_shift "$values" "$sep")
      # Perform the substitution
      subst="${subst/$arg/$val}"
    done
    echo "$subst"
  done < $tpl > $out
}

function help_and_quit {
  echo "Syntax is: $MYNAME [options]"
  echo "Where options are:"
  echo ""
  echo "-tpl <template input file>  Input file containing the template data"
  echo "-sep <separator string>     String surrounding each template parameter."
  echo "                            Example in '@@ARG@@', the string is '@@'"
  echo "-args <space separated list of args>"
  echo "                            Template argument strings"
  echo "-values <'sep' separated list of values>"
  echo "                            Values of each template argument"
  echo "                            to produce the instanciation"
  echo "-out <output file>          Output file to which the instanciation"
  echo "                            is recorded"
  echo ""
  echo "Quick example of command line:"
  echo "$MYNAME -tpl cpp-class.tpl -sep \"@@\" -args \"CLASSNAME\" \"AUTHOR\"\\"
  echo "   -values \"DataManager\"@@\"Andematunum\" -out \"DataManager.cpp\""
  echo ""
  
  # Exit error so that any invocation
  # triggering the help screen is considered
  # as a failure.
  exit 1
}

# ----
# Main
# ----

# At least 10 arguments is expected
if [ $# -lt 10 ]
then
  help_and_quit
fi

parse_args $*
# debug_args
check_args

apply_template

